package com.example.glideprogressiveloadimg.util

import java.io.IOException
import java.io.InputStream

class ProgressiveJpegParser {
    //----------------------mParserState的取值--------------------------------------------------------//
    /** Initial state of the parser. Next byte read by the parser should be 0xFF.  */
    //解析器的初始状态。解析器读取的下一个字节应该是0xFF。
    private val READ_FIRST_JPEG_BYTE = 0
    /** Parser saw only one byte so far (0xFF). Next byte should be second byte of SOI marker  */
    //解析器到目前为止只看到一个字节(0xFF)。下一个字节应该是SOI标记的第二个字节
    private val READ_SECOND_JPEG_BYTE = 1
    /**
     * Next byte is either entropy coded data or first byte of a marker. First byte of marker cannot
     * appear in entropy coded data, unless it is followed by 0x00 escape byte.
     * 下一个字节要么是熵编码的数据，要么是标记的第一个字节。
     * 标记的第一个字节不能出现在熵编码的数据中，除非它后面跟着0x00转义字节。
     */
    private val READ_MARKER_FIRST_BYTE_OR_ENTROPY_DATA = 2
    /**
     * Last read byte is 0xFF, possible start of marker (possible, because next byte might be "escape
     * byte" or 0xFF again) 最后读取的字节是0xFF，可能是标记的开始(可能，因为下一个字节可能是“转义字节”或再次是0xFF)
     */
    private val READ_MARKER_SECOND_BYTE = 3
    /**
     * Last two bytes constitute a marker that indicates start of a segment, the following two bytes
     * denote 16bit size of the segment 最后两个字节构成一个标记，指示段的开始，接下来的两个字节表示段的16位大小
     */
    private val READ_SIZE_FIRST_BYTE = 4
    /**
     * Last three bytes are marker and first byte of segment size, after reading next byte, bytes
     * constituting remaining part of segment will be skipped
     * 最后三个字节是标记字节和段大小的第一个字节，在读取下一个字节后，构成段剩余部分的字节将被跳过
     */
    private val READ_SIZE_SECOND_BYTE = 5

    /** Parsed data is not a JPEG file 解析到不是一个图片 */
    private val NOT_A_JPEG = 6
    //----------------------------------------------------------------------------------------------//

    /** The buffer size in bytes to use.  */
    private val BUFFER_SIZE = 16 * 1024

    private var mParserState = 0  //解析状态

    private var mLastByteRead = 0 //遍历输入流时，读取的上一个字节

    /** number of bytes consumed so far  到目前为止消耗的字节数  */
    private var mBytesParsed:Long = 0

    /** number of next fully parsed scan after reaching next SOS or EOI markers
     * 到达下一个SOS或EOI标记后再次完全解析扫描的次数 */
    private var mNextFullScanNumber = 0

    private var mBestScanNumber = 0
    private var mBestScanEndOffset = 0
    private var mEndMarkerRead = false

//    private var mByteArrayPool: ByteArrayPool? = null

//    fun ProgressiveJpegParser(byteArrayPool: ByteArrayPool?) {
    fun ProgressiveJpegParser() {
//        mByteArrayPool = Preconditions.checkNotNull(byteArrayPool)
        mBytesParsed = 0
        mLastByteRead = 0
        mNextFullScanNumber = 0
        mBestScanEndOffset = 0
        mBestScanNumber = 0
        mParserState = READ_FIRST_JPEG_BYTE
    }

    /**
     * If this is the first time calling this method, the buffer will be checked to make sure it
     * starts with SOI marker (0xffd8). If the image has been identified as a non-JPEG, data will be
     * ignored and false will be returned immediately on all subsequent calls.
     * 如果这是第一次调用这个方法，将检查缓冲区，以确保它从SOI标记(0xffd8)开始。如果图像被标识为非jpeg，数据将被忽略，
     * 并且在所有后续调用中将立即返回false。
     *
     * This object maintains state of the position of the last read byte. On repeated calls to this
     * method, it will continue from where it left off.
     * 该对象维护上次读取字节位置的状态。在重复调用这个方法时，它将从它停止的地方继续。
     *
     * @param encodedImage Next set of bytes received by the caller
     * @return true if a new full scan has been found
     */
//    fun parseMoreData(encodedImage: EncodedImage): Boolean {
    fun parseMoreData(bufferedDataStream: InputStream): Boolean {
        if (mParserState == NOT_A_JPEG) {
            return false
        }
//        val dataBufferSize: Int = encodedImage.getSize()
//        val available = bufferedDataStream.available()

        // Is there any new data to parse?
        // mBytesParsed might be greater than size of dataBuffer - that happens when
        // we skip more data than is available to read inside doParseMoreData method
//        if (dataBufferSize <= mBytesParsed) {
//            return false
//        }
//        val bufferedDataStream: InputStream = PooledByteArrayBufferedInputStream(
//            encodedImage.getInputStream(), mByteArrayPool.get(BUFFER_SIZE))
//        val bufferedDataStream: InputStream = PooledByteArrayBufferedInputStream(
//            encodedImage.getInputStream(), mByteArrayPool.get(BUFFER_SIZE), mByteArrayPool)
        return try {
            bufferedDataStream.reset()

            StreamUtil.skip(bufferedDataStream, mBytesParsed)  //跳过输入流 中已经解析过的字节数
            doParseMoreData(bufferedDataStream)
        } catch (ioe: IOException) {
            // Does not happen - streams returned by PooledByteBuffers do not throw IOExceptions
            if (ioe != null)
                throw RuntimeException(ioe)
            false
        } finally {
            Closeables.closeQuietly(bufferedDataStream)
        }
    }

    /**
     * Parses more data from inputStream.
     *
     * @param inputStream instance of buffered pooled byte buffer input stream
     */
    private fun doParseMoreData(inputStream: InputStream): Boolean {
        val oldBestScanNumber = mBestScanNumber
        try {
            var nextByte: Int = 0  //输入流下一个字节
            while (mParserState != NOT_A_JPEG && inputStream.read().also { nextByte = it } != -1) {
                mBytesParsed++  //已经解析的字节数，自增
                if (mEndMarkerRead) {  //一个标记结束
                    // There should be no more data after the EOI marker, just in case there is lets
                    // bail out instead of trying to parse the unknown data
                    //EOI标记之后应该没有更多的数据，以防万一有let bail out，而不是尝试解析未知数据
                    mParserState = NOT_A_JPEG
                    mEndMarkerRead = false
                    return false
                }

                when (mParserState) {
                    //初始状态
                    READ_FIRST_JPEG_BYTE -> mParserState =
                        if (nextByte == JfifUtil.MARKER_FIRST_BYTE) {
                            READ_SECOND_JPEG_BYTE
                        } else {
                            NOT_A_JPEG
                        }
                    //解析图片文件开头
                    READ_SECOND_JPEG_BYTE -> mParserState =
                        if (nextByte == JfifUtil.MARKER_SOI) { //0xD8
                            READ_MARKER_FIRST_BYTE_OR_ENTROPY_DATA
                        } else {
                            NOT_A_JPEG
                        }
                    //紧跟文件开头后面的就是metadata数据了,判断是否是开始标记
                    READ_MARKER_FIRST_BYTE_OR_ENTROPY_DATA -> if (nextByte == JfifUtil.MARKER_FIRST_BYTE) { //0xFF
                        mParserState = READ_MARKER_SECOND_BYTE
                    }
                    //
                    READ_MARKER_SECOND_BYTE -> if (nextByte == JfifUtil.MARKER_FIRST_BYTE) {  //0xFF, 跳过连续的FF字节
                        mParserState = READ_MARKER_SECOND_BYTE
                    } else if (nextByte == JfifUtil.MARKER_ESCAPE_BYTE) { //0x00,跳过填充数据， 返回上一个状态
                        mParserState = READ_MARKER_FIRST_BYTE_OR_ENTROPY_DATA
                    } else if (nextByte == JfifUtil.MARKER_EOI) {  //0xD9 , 图片结尾了， 结束此轮状态， 尝试获取一个个扫描开始
                        mEndMarkerRead = true
                        newScanOrImageEndFound(mBytesParsed.toInt() - 2) //标记本次scan的结束位置，
                        // There should be no data after the EOI marker, but in case there is, let's process
                        // the next byte as a first marker byte.
                        mParserState = READ_MARKER_FIRST_BYTE_OR_ENTROPY_DATA  // 返回上一个状态
                    } else {
                        if (nextByte == JfifUtil.MARKER_SOS) {  //0xDA, 一个扫描开始位置
                            newScanOrImageEndFound(mBytesParsed.toInt() - 2)
                        }
                        //0xDA判断标记之后跟随的是否是一个正常的数据段
                        mParserState = if (doesMarkerStartSegment(nextByte)) {
                            READ_SIZE_FIRST_BYTE  //进入下一个状态
                        } else {
                            READ_MARKER_FIRST_BYTE_OR_ENTROPY_DATA  //没有正常的数据的，返回状态2
                        }
                    }
                    //最后两个字节构成一个标记，指示段的开始，接下来的两个字节表示段的16位大小
                    READ_SIZE_FIRST_BYTE -> mParserState = READ_SIZE_SECOND_BYTE
                    READ_SIZE_SECOND_BYTE -> {      // shl(bits) – 有符号左移
                        val size = (mLastByteRead shl 8) + nextByte  //和上一个字节，组合到一起，得到一个size值
                        // We need to jump after the end of the segment - skip size-2 next bytes.
                        // We might want to skip more data than is available to read, in which case we will
                        // consume entire data in inputStream and exit this function before entering another
                        // iteration of the loop.
                        //我们需要在段的末尾之后跳转——跳过size-2的下一个字节。我们可能想要跳过比可读数据更多的数据，
                        // 在这种情况下，我们将消耗inputStream中的全部数据，并在进入另一个循环迭代之前退出这个函数。
                        val bytesToSkip: Long = (size - 2).toLong()
                        StreamUtil.skip(inputStream, bytesToSkip)
                        mBytesParsed += bytesToSkip
                        mParserState = READ_MARKER_FIRST_BYTE_OR_ENTROPY_DATA
                    }
                    NOT_A_JPEG -> Preconditions.checkState(false)
                    else -> Preconditions.checkState(false)
                }
                mLastByteRead = nextByte
            }
        } catch (ioe: IOException) {
            // does not happen, input stream returned by pooled byte buffer does not throw IOExceptions
//            Throwables.propagate(ioe)
            if (ioe != null)
                throw RuntimeException(ioe)
        }
        return mParserState != NOT_A_JPEG && mBestScanNumber != oldBestScanNumber
    }

    /** Not every marker is followed by associated segment
     * 并不是每个标记后面都跟着相关的段  */
    private fun doesMarkerStartSegment(markerSecondByte: Int): Boolean {
        if (markerSecondByte == JfifUtil.MARKER_TEM) {
            return false
        }
        return if (markerSecondByte >= JfifUtil.MARKER_RST0 && markerSecondByte <= JfifUtil.MARKER_RST7) {
            false
        } else markerSecondByte != JfifUtil.MARKER_EOI && markerSecondByte != JfifUtil.MARKER_SOI
    }

    /**
     * 找到新的Scan，或者是图片到达文件结束位置
     *
     * 标记本次scan的结束位置
     * 统计全部的scan的次数
     * 指定当前scan的索引
     */
    private fun newScanOrImageEndFound(offset: Int) {
        if (mNextFullScanNumber > 0) {
            mBestScanEndOffset = offset
        }
        mBestScanNumber = mNextFullScanNumber++
    }

    fun isJpeg(): Boolean {
        return mBytesParsed > 1 && mParserState != NOT_A_JPEG
    }

    /** @return offset at which parsed data should be cut to decode best available partial result
     */
    fun getBestScanEndOffset(): Int {
        return mBestScanEndOffset
    }

    /** @return number of the best scan found so far
     */
    fun getBestScanNumber(): Int {
        return mBestScanNumber
    }

    /** Returns true if the end marker has been read.  */
    fun isEndMarkerRead(): Boolean {
        return mEndMarkerRead
    }

    //----------------------------------------------------------------------------------//
}